#pragma

#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>
#include "Log.hpp"


//将套接字与业务程序进行解耦,用一个Sock类来封装，直接调用类的接口，这样更加的直观
class Sock
{
private:
    const static int gbacklog=20;//listen等待队列的长度
public:
    //1.构造函数
    Sock(){}
    //2.析构函数
    ~Sock(){}
    
    //3.用于获取一个套接字
    static int Socket()
    {
        int listensock=socket(AF_INET,SOCK_STREAM,0);//协议家族，套接字类型，
        if(listensock<0)
        {
            logMessage(FATAL,"create socket error, %d : %s",errno,strerror(errno));
            exit(2);
        }
        logMessage(NORMAL,"create socket success, listensock : %d",listensock);
        return listensock;
    }

    static void Bind(int sock,uint16_t port,std::string ip="0.0.0.0")//套接字，需要绑定的端口，需要绑定的ip
    {
        struct sockaddr_in local;
        memset(&local,0,sizeof local);
        local.sin_family=AF_INET;
        local.sin_port=htons(port);
        inet_pton(AF_INET,ip.c_str(),&local.sin_addr);
        // local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        logMessage(FATAL,"listensock bind success...");

        if(bind(sock,(struct sockaddr*)&local,sizeof local)<0)
        {
            logMessage(FATAL,"listensock bind error, %d : %s",errno,strerror(errno));
            exit(3);
        }
    }

    static void Listen(int listensock)//将套接字设置为监听状态，设置监听队列，可以让来到的连接在监听队列中等待获取上来派发服务
    {
        if(listen(listensock,gbacklog)<0)
        {
            logMessage(FATAL,"listensock listen error, %d : %s",errno,strerror(errno));
            exit(4);
        }
        logMessage(NORMAL,"listensock listen success");
    
    }

    // 一般经验
    // const std::string &: 输入型参数
    // std::string *: 输出型参数
    // std::string &: 输入输出型参数


    //将监听队列中的连接获取起来派发服务
    //用于获取客户端的连接，获取客户端的信息，然后派发serversock给这个指定的客户端的进行服务
    static int Accept(int listensock,std::string *ip,uint16_t* port)
    {
        struct sockaddr_in src;//用于获取客户端的信息
        socklen_t len=sizeof(src);
        int servicesock =accept(listensock,(struct sockaddr*)&src,&len);
        if(servicesock<0)
        {
            logMessage(ERROR,"accept error, %d : %s",errno,strerror(errno));
            return -1;
        }
        if(port)*port=ntohs(src.sin_port);
        if(ip)*ip=inet_ntoa(src.sin_addr);
        return servicesock;
    }
    
    //目的是为了将服务端与该套接字进行连接
    //使得服务端可以通过该套接字向网络中发送信息
    static bool Connect(int servicesock,const std::string& server_ip,const uint16_t &server_port)
    {
        struct sockaddr_in server;//用于存储服务端的信息
        memset(&server,0,sizeof(server));
        server.sin_family=AF_INET;
        server.sin_port=htons(server_port);
        server.sin_addr.s_addr=inet_addr(server_ip.c_str());

        if(connect(servicesock,(struct sockaddr*)&server,sizeof(server))==0)return true;
        else return false;
    }
};